package com.qdev.apps.core.persistence.impl;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import net.sf.hibernate.Criteria;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.MappingException;
import net.sf.hibernate.Session;
import net.sf.hibernate.engine.QueryParameters;
import net.sf.hibernate.engine.SessionFactoryImplementor;
import net.sf.hibernate.engine.SessionImplementor;
import net.sf.hibernate.impl.CriteriaImpl;
import net.sf.hibernate.impl.SessionFactoryImpl;
import net.sf.hibernate.loader.CriteriaLoader;
import net.sf.hibernate.persister.ClassPersister;
import net.sf.hibernate.persister.OuterJoinLoadable;
import net.sf.hibernate.type.IntegerType;
import net.sf.hibernate.type.Type;

/**
 * counts the results of a given Criteria; mostly used for pagination. was
 * copied from http://forum.hibernate.org/viewtopic.php?t=926203
 * 
 * @author Quake Wang
 * @since 2004-7-30
 * @version $Revision: 1.2 $
 */
public class CriteriaResultsCounter extends CriteriaLoader {

    protected CriteriaResultsCounter(OuterJoinLoadable persister, SessionFactoryImplementor factory, CriteriaImpl criteria) throws HibernateException {
        super(persister, factory, criteria);
    }

    public static int count(Session s, Criteria criteria) throws HibernateException {
        CriteriaImpl cImpl = (CriteriaImpl) criteria;
        SessionImplementor sImpl = (SessionImplementor) s;
        SessionFactoryImpl sfImpl = (SessionFactoryImpl) s.getSessionFactory();
        Class clazz = cImpl.getCriteriaClass();
        ClassPersister ps = sfImpl.getPersister(clazz);
        if (!(ps instanceof OuterJoinLoadable)) {
            throw new MappingException("class persister is not OuterJoinLoadable: " + clazz.getName());
        }
        OuterJoinLoadable ojl = (OuterJoinLoadable) ps;
        CriteriaResultsCounter counter = new CriteriaResultsCounter(ojl, sfImpl, cImpl);
        int count = counter.doCount(sImpl);
        return count;
    }

    protected void setSql() throws MappingException {
        String select = getSQLString();
        int start = select.indexOf(" from ");
        String from = select.substring(start, select.length());
        String count = "select count(*) ";
        sql = count + from;
    }

    protected int doCount(SessionImplementor sImpl) throws HibernateException {
        try {
            setSql();
            List l = list(sImpl);
            Iterator i = l.iterator();
            int count = ((Integer) i.next()).intValue();
            return count;
        } catch (SQLException e) {
            throw new HibernateException(e);
        }
    }

    protected List list(final SessionImplementor session, final QueryParameters queryParameters, final Set querySpaces, final Type[] resultType)
            throws SQLException, HibernateException {
        Type[] countType = new Type[1];
        countType[0] = new IntegerType();
        final SessionFactoryImplementor factory = session.getFactory();

        PreparedStatement st = prepareQueryStatement(applyLocks(getSQLString(), queryParameters.getLockModes(), session.getFactory().getDialect()),
                queryParameters, false, session);

        List list = new ArrayList();
        ResultSet rs = st.executeQuery();
        if (rs.next()) {
            int count = rs.getInt(1);
            list.add(new Integer(count));
        }
        return list;
    }
}